home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Graphics 2D / Super Snapshot / Snapshot.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-10-06  |  22.3 KB  |  772 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        Snapshot.c
  3.  
  4.     Contains:    This application demonstrates how to quickly and        
  5.                 efficiently capture the main device's desktop into            
  6.                 a window.  The program basically reads the image             
  7.                 stored in the the main device's pixmap then copies            
  8.                 it to a custom pixmap.  The custom pixmap is de-            
  9.                 fined at the same depth of the main device and                 
  10.                 contains an identical copy of that device's color-            
  11.                 table.  This is done to provide the fastest                 
  12.                 performance possible when copying from an offscreen            
  13.                 to onscreen pixmap.  By making sure the pixel values        
  14.                 map to the exact same colors in both colortables,            
  15.                 copybits will do a direct transfer of bits without            
  16.                 wasting time remapping the colors.  Also the ctSeed            
  17.                 field for each colortable should be the same.  Finally,        
  18.                 since the main device's bounding rect is different            
  19.                 than that of the offscreen's, the copying performance        
  20.                 for the device to the offscreen is slightly affected        
  21.                 because of the scaling required.  However, the copying        
  22.                 performance for the offscreen to the window is the             
  23.                 best possible since the bounding rects for each are            
  24.                 identical.                                        
  25.  
  26.     Written by: EL    
  27.  
  28.     Copyright:    Copyright © 1991-1999 by Apple Computer, Inc., All Rights Reserved.
  29.  
  30.                 You may incorporate this Apple sample source code into your program(s) without
  31.                 restriction. This Apple sample source code has been provided "AS IS" and the
  32.                 responsibility for its operation is yours. You are not permitted to redistribute
  33.                 this Apple sample source code as "Apple sample source code" after having made
  34.                 changes. If you're going to re-distribute the source, we require that you make
  35.                 it clear in the source that the code was descended from Apple sample source
  36.                 code, but that you've made changes.
  37.  
  38.     Change History (most recent first):
  39.                 08/2000        JM                Major revisions made. In addition to being "carbonized"
  40.                                             added: multiple windows, refreshing, saving, conditional
  41.                                             menu bar for OS X and 9, Apple Event Quit support,
  42.                                             updated about box....(bunch of extra things)
  43.                 11/6/1999    GGS                 Updated to work with modern (1999) Mac OS.  
  44.                                             Fixed a PixMap disposing bug.  Updated casts
  45.                                             and headers.
  46.                 7/14/1999    KG                Updated for Metrowerks Codewarror Pro 2.1                
  47. */
  48.  
  49. #include "Snapshot.h"
  50. #include <PLStringFuncs.h>
  51.  
  52. // Different picture scaling constants
  53. const int FULL_SIZE = 1;
  54. const float HALF_SIZE = 1.41; // square root of 2, roughly
  55. const int QUARTER_SIZE = 2;
  56.  
  57. /* Global Variable Definitions */
  58.  
  59. Rect                gBounds;                        // System resolution, effectively
  60. Boolean                gDone = false;                    // Application termination global
  61. Boolean                gDoingTrick = false;            // "Trick" flag
  62. float                gScale = QUARTER_SIZE;            // Scaling the window, relative to resolution (default of 1/2 width and height)
  63. Rect                gMinWindowSize;                    // The minimum window size
  64. const unsigned char*        fileName =     "\pDesktop Pic";    // Generic filename
  65. const unsigned char*        defaultName = "\pSnapshot";        // Default Save File Name
  66. unsigned long        resizingDelay = 0;
  67.  
  68. void main()
  69. {    
  70.     initMac();
  71.     setUp();
  72.     
  73.     doNewSnapshot();
  74.     adjustMenus();
  75.     doEventLoop();
  76.     destroyAllWindows();
  77. }
  78.  
  79. // Tests to see if we are running on Mac OS X.
  80. Boolean onOSX()
  81. {
  82.     long response;
  83.     OSErr    anErr = noErr;
  84.     
  85.     anErr = Gestalt(gestaltSystemVersion, &response);
  86.     
  87.     return response >= 0x01000 && (anErr == noErr);
  88. }
  89.  
  90. void destroyAllWindows()
  91. {
  92.     WindowPtr theWindow = FrontWindow();
  93.     while(FrontWindow() != NULL) {
  94.         disposeWindow(theWindow);
  95.         theWindow = FrontWindow();    
  96.     }
  97. }
  98.  
  99. void initMac()
  100. {
  101.     // the only "init" calls needed in carbon
  102.     InitCursor();
  103.     FlushEvents( 0, everyEvent );
  104. }
  105.  
  106. void setUp()
  107. {
  108.     Handle    menuBar;
  109.     OSErr     anErr = noErr;
  110.     long    response;
  111.     
  112.     // Carbon Porting guidelines say provide alternate menu bar/menu scheme for OS X
  113.     // This is just one way of doing a different menu for 9 and X, which is pretty static
  114.     if (onOSX()) 
  115.         menuBar = GetNewMBar(MENU_BAR_IDX);        //if we are running on X, need menu without a File->Quit
  116.     else
  117.         menuBar = GetNewMBar(MENU_BAR_ID);        //default menu bar
  118.         
  119.     anErr = ResError();
  120.     
  121.     if ( menuBar == nil || anErr != noErr )
  122.          ExitToShell();    
  123.  
  124.     SetMenuBar(menuBar);
  125.     DisposeHandle(menuBar);
  126.  
  127.     DrawMenuBar();
  128.     // Install 'quit' event handler
  129.     // This "handler" is called when a user selects Application->Quit on OS X.
  130.     if ((Gestalt(gestaltAppleEventsAttr, &response) == noErr)) {
  131.             anErr = AEInstallEventHandler(kCoreEventClass, kAEQuitApplication,
  132.                      NewAEEventHandlerUPP(AEQuitHandler), 0, false);
  133.             if (anErr != noErr)  
  134.                 ExitToShell();
  135.     }
  136. }
  137.  
  138. void doNewSnapshot()
  139. {
  140.     WindowPtr        newWindow = nil;
  141.     PixMapHandle    pixHandle = nil;
  142.     // Snapshots consist of a window and a PixMap, could have chosen
  143.     // to use only pictures instead, but the existing code dealt with
  144.     // PixMaps, so I kept the existing "concept"
  145.     
  146.     pixHandle = createScreenPixMap();
  147.     newWindow = createWindow();
  148.     // Window's refCon is its handle to a PixMap
  149.     SetWRefCon(newWindow, (long)pixHandle);
  150.     drawImage( newWindow );
  151.     ShowWindow( newWindow );
  152. }
  153.  
  154. WindowPtr createWindow()
  155. {
  156.     Rect     wBounds;
  157.     BitMap    bitMap;
  158.     int        top, left, width, height;
  159.     WindowPtr newWindow;
  160.     
  161.     // Centering the window is a good thing....
  162.  
  163.     GetQDGlobalsScreenBits(&bitMap);
  164.  
  165.     width = ((bitMap.bounds.right - bitMap.bounds.left) / gScale);
  166.     height = ((bitMap.bounds.bottom - bitMap.bounds.top) / gScale);
  167.  
  168.     left = (((bitMap.bounds.right - bitMap.bounds.left) - width) / 2);
  169.     top = (((bitMap.bounds.bottom - bitMap.bounds.top) - height) / 2);
  170.     
  171.     // if scale is same size or bigger than the screen
  172.     if (gScale <= FULL_SIZE)
  173.         top = left = GetMBarHeight() * 3; //arbitrary position....
  174.     
  175.     // Create a window to display the final offscreen image. 
  176.     SetRect( &wBounds, left, top, left + width, top + height );
  177.     
  178.     newWindow = NewCWindow( 0L, &wBounds, "\pSuper Snapshot!", false, kWindowFullZoomGrowDocumentProc,
  179.                             (WindowPtr)-1L, true, 0L );
  180.     // The refcon of the windows are going to be used to store the PixMapHandle
  181.     // from a screen capture.  0 means no associated PixMapHandle while non-zero
  182.     // means the value is a handle to a PixMap. A windows refcon should always be
  183.     // a value in this program
  184.     SetWRefCon(newWindow, 0L);
  185.     return newWindow;
  186. }
  187.  
  188. void disposeWindow(WindowPtr dieWindow)
  189. {
  190.     PixMapHandle pixHandle;
  191.     
  192.     if (dieWindow == NULL || !IsValidWindowPtr(dieWindow))
  193.         return;
  194.     
  195.     // Free up the baseAddr pointer field, the PixMap itself,
  196.     // and the window
  197.     pixHandle = (PixMapHandle)GetWRefCon(dieWindow);
  198.     if (pixHandle != nil) {
  199.         LockPixels(pixHandle);
  200.         DisposePtr((*pixHandle)->baseAddr);
  201.         DisposeCTable((*pixHandle)->pmTable);
  202.         // set the color table to nil since this was a "custom"
  203.         // pixmap, otherwise DisposePixMap will not deallocate the memory
  204.         (*pixHandle)->pmTable = nil;
  205.         UnlockPixels(pixHandle);
  206.         DisposePixMap(pixHandle);
  207.     }
  208.     DisposeWindow(dieWindow);
  209. }
  210.  
  211. void calculateSystemBounds()
  212. {
  213.     BitMap    bitMap;
  214.     
  215.     // Get the resolution of the screen, save it in a global Rectangle and 
  216.     // use the info to calculate the min/max window size
  217.  
  218.     GetQDGlobalsScreenBits(&bitMap);
  219.  
  220.     SetRect( &gBounds, 0, 0, bitMap.bounds.right, bitMap.bounds.bottom);
  221.     SetRect(&gMinWindowSize, bitMap.bounds.right / 4, bitMap.bounds.bottom / 4, bitMap.bounds.right, bitMap.bounds.bottom);
  222. }
  223.  
  224. PixMapHandle createScreenPixMap()
  225. {
  226.     GDHandle    mainDevice;
  227.     CTabHandle    cTable;
  228.     short        depth;    
  229.     Ptr            offBaseAddr;    /* Pointer to the off-screen pixel image */
  230.     short        bytesPerRow;
  231.     PixMapHandle    pixHandle = nil;
  232.     
  233.     pixHandle = NewPixMap();
  234.     
  235.     /* Get a handle to the main device. */
  236.     mainDevice = GetMainDevice();
  237.  
  238.     /* Store its current pixel depth. */
  239.     depth = (**(**mainDevice).gdPMap).pixelSize;
  240.  
  241.     /* Make an identical copy of its pixmap's colortable. */
  242.     cTable = (**(**mainDevice).gdPMap).pmTable;
  243.     HandToHand( &(Handle)cTable );
  244.     
  245.     // Get Resolution of screen
  246.     calculateSystemBounds();
  247.     
  248.     // Fill in a few of the PixMap's fields...
  249.     // NewPixMap() is good for default initialization, simply modify
  250.     // the new PixMap
  251.     (*pixHandle)->pmTable = cTable;
  252.     (*pixHandle)->bounds = gBounds;
  253.     (*pixHandle)->pixelSize = depth;
  254.     bytesPerRow = ((gBounds.right - gBounds.left) * depth) / 8;
  255.     offBaseAddr = NewPtr((unsigned long) bytesPerRow * (gBounds.bottom - gBounds.top));
  256.     (*pixHandle)->baseAddr = offBaseAddr;              // Point to image
  257.     (*pixHandle)->rowBytes = bytesPerRow | 0x8000;    // MSB set for PixMap
  258.     
  259.     LockPixels(pixHandle);
  260.  
  261.     CopyBits( (BitMap *)*(**mainDevice).gdPMap, (BitMap *) *pixHandle,
  262.                 &(**(**mainDevice).gdPMap).bounds, &(*pixHandle)->bounds, srcCopy, 0l );
  263.     
  264.     UnlockPixels(pixHandle);
  265.     
  266.     return pixHandle;
  267. }
  268.  
  269. void drawImage(WindowPtr theWindow)
  270. {
  271.     Rect tempRect1;
  272.     PixMapHandle    pixHandle = (PixMapHandle) GetWRefCon(theWindow);
  273.     GrafPtr            oldPort;
  274.     
  275.     GetPort(&oldPort);
  276.     SetPortWindowPort(theWindow);
  277.     if (theWindow == NULL || !IsValidWindowPtr(theWindow))
  278.         return;
  279.     if (pixHandle == NULL)
  280.         return;
  281.     else
  282.         LockPixels(pixHandle);
  283.     // Copy the offscreen image back onto the window.
  284.  
  285.     CopyBits( (BitMap *) *pixHandle,
  286.         GetPortBitMapForCopyBits(GetWindowPort(theWindow)),
  287.         &(*pixHandle)->bounds,
  288.         GetPortBounds(GetWindowPort(theWindow),&tempRect1),
  289.         srcCopy, 0L);
  290.  
  291.     UnlockPixels(pixHandle);
  292.     SetPort(oldPort);
  293. }
  294.  
  295. void saveToPICTFile(WindowPtr theWindow)
  296. {
  297.     PicHandle            picHandle;
  298.     OSErr                anErr = noErr;
  299.     NavReplyRecord      reply;
  300.     NavDialogOptions    dialogOptions;
  301.     FSSpec              documentFSSpec;
  302.     OSType              fileTypeToSave = 'PICT';
  303.     OSType              creatorType = 'ogle';        // PictureViewer
  304.     AEKeyword           theKeyword;
  305.     DescType            actualType;
  306.     Size                actualSize;
  307.     Rect                tempRect1;
  308.     PixMapHandle        pixHandle;
  309.     
  310.     // This is a very basic NavServices file-saving example
  311.     // added logic for this program specifically
  312.     
  313.     if (theWindow == NULL || !IsValidWindowPtr(theWindow))
  314.         return;
  315.     
  316.     pixHandle = (PixMapHandle) GetWRefCon(theWindow);
  317.     LockPixels(pixHandle);
  318.     
  319.     SetPortWindowPort(theWindow);
  320.     
  321.     GetPortBounds(GetWindowPort(theWindow), &tempRect1);
  322.     picHandle = OpenPicture(&tempRect1);
  323.     
  324.     CopyBits((BitMap*) *pixHandle, GetPortBitMapForCopyBits(GetWindowPort(theWindow)), &(*pixHandle)->bounds, 
  325.      &tempRect1, srcCopy, 0L);
  326.      
  327.     ClosePicture();
  328.  
  329.     anErr = NavGetDefaultDialogOptions(&dialogOptions); 
  330.     dialogOptions.dialogOptionFlags |= kNavSelectDefaultLocation;
  331.     
  332.     // Set default name for a saved snapshot
  333.     PLstrcpy(dialogOptions.savedFileName, defaultName);
  334.     
  335.     anErr = NavPutFile( nil, 
  336.                         &reply, 
  337.                         &dialogOptions, 
  338.                         nil,
  339.                         fileTypeToSave, 
  340.                         creatorType, 
  341.                         nil );
  342.  
  343.     if (anErr == noErr && reply.validRecord) {
  344.         anErr = AEGetNthPtr(&(reply.selection), 1, typeFSS,
  345.                                 &theKeyword, &actualType,
  346.                                 &documentFSSpec, sizeof(documentFSSpec),
  347.                                 &actualSize );
  348.         if (anErr == noErr) {
  349.                 writePictToFile(&documentFSSpec, picHandle);
  350.             }
  351.             reply.translationNeeded = false;
  352.            anErr = NavCompleteSave(&reply, kNavTranslateInPlace);
  353.     
  354.            NavDisposeReply(&reply);
  355.     }
  356.     KillPicture(picHandle);
  357.     UnlockPixels(pixHandle);
  358. }
  359.  
  360. void writePictToFile(FSSpec *fspec, PicHandle picHandle)
  361. {
  362.     OSErr                 anErr = noErr;
  363.     long                inOutCount;
  364.     short                refNum;
  365.     OSType              fileTypeToSave = 'PICT';
  366.     OSType              creatorType = 'ogle';        // PictureViewer
  367.     int                    count;
  368.     unsigned char         header[512];
  369.     
  370.     // Pict files have to have 512 bytes of "zero" data at the front.
  371.     for (count = 0; count < 512; count++)
  372.         header[count] = 0x00;
  373.  
  374.     anErr = FSpCreate(fspec, creatorType, fileTypeToSave, smSystemScript);
  375.     if (anErr == dupFNErr) {
  376.         anErr = FSpDelete(fspec);    //delete file if already exists
  377.         anErr = FSpCreate(fspec, creatorType, fileTypeToSave, smSystemScript);
  378.     }
  379.             
  380.     // write the file
  381.     FSpOpenDF(fspec, fsRdWrPerm, &refNum );
  382.     inOutCount = 512;
  383.        anErr = FSWrite(refNum, &inOutCount, header);        // write the header
  384.     if (anErr == noErr) {
  385.         inOutCount = GetHandleSize((Handle)picHandle);
  386.         anErr = FSWrite(refNum,&inOutCount,*picHandle);
  387.        }
  388.     FSClose( refNum );
  389. }
  390.  
  391. void doTrickEventLoop()
  392. {
  393.     EventRecord    anEvent;
  394.     WindowPtr    evtWind = NULL;
  395.     
  396.     // Event loop while doing the "full screen" trick
  397.     
  398.     while (!gDone) {
  399.         if (WaitNextEvent( everyEvent, &anEvent, 2, nil )) {
  400.             if (anEvent.what == updateEvt) {
  401.                 evtWind = (WindowPtr)anEvent.message;    
  402.                 SetPortWindowPort(evtWind);
  403.                     
  404.                 BeginUpdate(evtWind);
  405.                 drawImage(evtWind);
  406.                 EndUpdate(evtWind);
  407.             }
  408.             else if (anEvent.what == autoKey || anEvent.what == keyDown)
  409.                 handleKeyPress(&anEvent);
  410.             else if (anEvent.what == kHighLevelEvent)
  411.                 AEProcessAppleEvent(&anEvent);
  412.             }
  413.     }
  414.     gDone = false;
  415. }
  416. void doConfusion()
  417. {
  418.     int             menuBarHeight = GetMBarHeight();
  419.     GDHandle        mainDevice;
  420.     PicHandle        picHandle;
  421.     FSSpec            fspec;
  422.     PixMapHandle    pixHandle;
  423.     WindowPtr        theWindow;
  424.     float            oldScale = gScale;
  425.     OSErr            anErr = noErr;
  426.     AEDesc            desc;
  427.     Ptr                oldState = NULL;
  428.     WindowPtr        bigWindow;
  429.     RgnHandle        rgnHandle = NewRgn();
  430.     Rect            tempRect1;
  431.  
  432.     gScale = 1;
  433.     
  434.     mainDevice = GetMainDevice();
  435.     
  436.     pixHandle = createScreenPixMap();
  437.     LockPixels(pixHandle);
  438.     
  439.     if (!onOSX()) {
  440.         // used a window for debugging purposes
  441.         theWindow = createWindow();
  442.         SetPortWindowPort(theWindow);
  443.         SetWRefCon(theWindow, (long) pixHandle);
  444.         picHandle = OpenPicture(&gBounds);
  445.         drawImage(theWindow);
  446.         ClosePicture();
  447.         //ShowWindow(theWindow);
  448.     
  449.         // write pict to file
  450.         FSMakeFSSpec(0, 0, fileName, &fspec);
  451.         writePictToFile(&fspec, picHandle);
  452.     
  453.         // set desktop pict :-)
  454.         anErr = OHMakeAliasDescFromFSSpec(&fspec, &desc);
  455.         if (anErr == noErr) {
  456.             SetDesktopPict(&desc, 1);
  457.             AEDisposeDesc(&desc);
  458.         }
  459.         KillPicture(picHandle);
  460.         DisposeWindow(theWindow);
  461.         gDone = true;
  462.     }
  463.     else {
  464.         // since OS X does not use the Appearance manager for the desktop background
  465.         // we will drop into full screen mode instead
  466.         gDoingTrick = true;
  467.         BeginFullScreen(&oldState, nil, 0, 0, &bigWindow, 0, fullScreenAllowEvents);
  468.         SetWRefCon(bigWindow, (long)pixHandle);
  469.         SetPortWindowPort(bigWindow);
  470.  
  471.         CopyBits( (BitMap *) *pixHandle,
  472.             GetPortBitMapForCopyBits(GetWindowPort(bigWindow)),
  473.             &(*pixHandle)->bounds,
  474.             GetPortBounds(GetWindowPort(bigWindow),&tempRect1),
  475.             srcCopy, 0L);
  476.         QDFlushPortBuffer(GetWindowPort(bigWindow), GetPortVisibleRegion(GetWindowPort(bigWindow), rgnHandle));
  477.  
  478.         doTrickEventLoop();
  479.         EndFullScreen(oldState, nil);
  480.         gDoingTrick = false;
  481.     }
  482.     
  483.     // clean up
  484.     DisposePtr((*pixHandle)->baseAddr);
  485.     DisposeCTable((*pixHandle)->pmTable);
  486.     UnlockPixels(pixHandle);
  487.     DisposePixMap(pixHandle);
  488.     DisposeRgn(rgnHandle);
  489.     gScale = oldScale;
  490. }
  491.  
  492.  
  493. void adjustMenus()
  494. {
  495.     // Function simply recalculates menus based on app state
  496.     // Apps should do better menu handling, this is quick and dirty :-)
  497.     
  498.     MenuHandle menuHandle = GetMenuHandle(SIZE_MENU);
  499.     int    counter;
  500.  
  501.     for (counter = 1; counter <= NUMBER_OF_SIZES; counter++)
  502.         CheckMenuItem(menuHandle, counter, false);
  503.     
  504.     if (gScale == QUARTER_SIZE)
  505.         CheckMenuItem(menuHandle, SIZE_QUARTER_SCALE, true);
  506.     else if (gScale == HALF_SIZE)
  507.         CheckMenuItem(menuHandle, SIZE_HALF_SCALE, true);
  508.     else if (gScale == FULL_SIZE)
  509.         CheckMenuItem(menuHandle, SIZE_FULL_SCALE, true);
  510.  
  511.     menuHandle = GetMenuHandle(onOSX() ? FILE_MENUX : FILE_MENU);
  512.     
  513.     for (counter = 2; counter <= NUMBER_OF_FILE_MENU_ITEMS; counter++)
  514.         FrontWindow() != NULL ? EnableMenuItem(menuHandle, counter) : DisableMenuItem(menuHandle, counter);
  515. }
  516.  
  517. void resizeWindow(WindowPtr theWindow)
  518. {
  519.     PixMapHandle pixHandle;
  520.     Rect    originalLoc, newLoc;
  521.     Boolean    wasCollapsed;
  522.     RgnHandle    rgnHandle = NewRgn();
  523.     
  524.     // Resize the window....
  525.     if (theWindow == NULL || !IsValidWindowPtr(theWindow))
  526.         return;
  527.     
  528.     wasCollapsed = IsWindowCollapsed(theWindow);
  529.     
  530.     if (wasCollapsed) {
  531.         HideWindow(theWindow);
  532.         CollapseWindow(theWindow, false);
  533.     }
  534.     
  535.     pixHandle = (PixMapHandle)GetWRefCon(theWindow);
  536.     GetWindowBounds(theWindow, kWindowContentRgn, &originalLoc);
  537.     
  538.     SetRect(     &newLoc, 
  539.                 originalLoc.left, originalLoc.top, 
  540.                 gBounds.right / gScale + originalLoc.left,
  541.                 gBounds.bottom / gScale + originalLoc.top);
  542.     SetWindowBounds(theWindow, kWindowContentRgn, &newLoc);
  543.     drawImage(theWindow);
  544.     QDFlushPortBuffer(    GetWindowPort(theWindow), 
  545.                         GetPortVisibleRegion(GetWindowPort(theWindow), rgnHandle));
  546.     
  547.     if (wasCollapsed) {
  548.         CollapseWindow(theWindow, wasCollapsed);
  549.         ShowWindow(theWindow);
  550.     }
  551.     DisposeRgn(rgnHandle);
  552. }
  553.  
  554. void handleMenuSelection(long result)
  555. {
  556.     int menuID, menuItem;
  557.     RgnHandle rgnHandle = NewRgn();
  558.     DialogPtr    theDialog = NULL;
  559.     short itemHit;
  560.     PixMapHandle    pixHandle;
  561.     
  562.     menuID = HiWord(result);
  563.     menuItem = LoWord(result);
  564.     HiliteMenu(0);
  565.     
  566.     // File Menu
  567.     if (menuID == FILE_MENU || menuID == FILE_MENUX) {
  568.         if (menuItem == FILE_SAVE)
  569.             saveToPICTFile(FrontWindow());
  570.         else if (menuItem == FILE_QUIT)
  571.             gDone = true;
  572.         else if (menuItem == FILE_CLOSE) {
  573.             if (gDoingTrick)
  574.                 gDone = true;
  575.             else if (FrontWindow() != NULL)
  576.                 disposeWindow(FrontWindow());
  577.         }
  578.         else if (menuItem == FILE_NEW) {
  579.             doNewSnapshot();
  580.         }
  581.         else if (menuItem == FILE_REFRESH) {
  582.             if (FrontWindow() == NULL) {
  583.                 HiliteMenu(0);
  584.                 return;
  585.             }
  586.             pixHandle = (PixMapHandle)GetWRefCon(FrontWindow());
  587.             DisposePtr((**pixHandle).baseAddr);
  588.             DisposePixMap(pixHandle);
  589.             pixHandle = createScreenPixMap();
  590.             SetWRefCon(FrontWindow(), (long)pixHandle);
  591.             drawImage(FrontWindow());
  592.             QDFlushPortBuffer(GetWindowPort(FrontWindow()), GetPortVisibleRegion(GetWindowPort(FrontWindow()), rgnHandle));
  593.         }
  594.     }
  595.     else if (menuID == ABOUT_MENU) {    //about menu handling
  596.         if (menuItem == ABOUT) {
  597.             theDialog = GetNewDialog ( ABOUTDLG, nil, (WindowPtr)-1 );
  598.                     do {
  599.                         ModalDialog ( nil, &itemHit );
  600.                     } while( itemHit != ok ) ;
  601.                     DisposeDialog ( theDialog );
  602.         }
  603.     }
  604.     else if (menuID == SIZE_MENU) {        //size menu
  605.         switch (menuItem) {
  606.             case SIZE_QUARTER_SCALE:
  607.                 gScale = QUARTER_SIZE;
  608.                 break;
  609.             case SIZE_HALF_SCALE:
  610.                 gScale = HALF_SIZE;
  611.                 break;
  612.             case SIZE_FULL_SCALE:
  613.                 gScale = FULL_SIZE;
  614.                 break;
  615.         }
  616.         resizeWindow(FrontWindow());
  617.     }
  618.     else if (menuID == SPECIAL_MENU || menuID == SPECIAL_MENUX) { // "special" because depends on OS 9 or X
  619.         switch (menuItem) {
  620.             case SPECIAL_CONFUSING:
  621.                 doConfusion();
  622.             break;
  623.         }
  624.     }
  625.     adjustMenus();
  626.     DisposeRgn(rgnHandle);
  627. }
  628.  
  629. void handleKeyPress(EventRecord *event)
  630. {
  631.     char    key;
  632.  
  633.     key = event->message & charCodeMask;
  634.     
  635.     // just check to see if the command key is down, if so
  636.     // process it as menu selection
  637.     if ( event->modifiers & cmdKey )
  638.         handleMenuSelection(MenuKey(key));
  639. }
  640.  
  641. // Apple Event - "Quit" Handler
  642. pascal OSErr AEQuitHandler(const AppleEvent *messagein, AppleEvent *reply, unsigned long refIn)
  643. {
  644.     #pragma unused (messagein,refIn,reply)
  645.     
  646.     // Do not call ExitToShell() in an AE Handler....
  647.     gDone = true;
  648.     return noErr;
  649. }
  650.  
  651. void doDynamicResizing(WindowPtr theWindow)
  652. {
  653.     Point    currentLoc;
  654.     Rect    windowBounds;
  655.     long    time;
  656.     int offsetX, offsetY;
  657.     int    windowWidth, windowHeight;
  658.     RgnHandle    rgnHandle = NewRgn();
  659.     EventRecord    anEvent;
  660.     
  661.     // I could not find any code that would handle dynamic resizing using the "classic"
  662.     // or traditional event model....so I wrote my own implementation
  663.     GetWindowBounds(theWindow, kWindowStructureRgn, &windowBounds);
  664.     GetMouse(¤tLoc);
  665.     LocalToGlobal(¤tLoc);
  666.     
  667.     // calculate where mouse is in relation to edge of the window
  668.     offsetX = windowBounds.right - currentLoc.h;
  669.     offsetY = windowBounds.bottom - currentLoc.v;
  670.     time = TickCount();
  671.     while (Button()) {
  672.         GetMouse(¤tLoc);
  673.         LocalToGlobal(¤tLoc);
  674.         GetWindowBounds(theWindow, kWindowStructureRgn, &windowBounds);
  675.         windowBounds.bottom = currentLoc.v + offsetX;
  676.         windowBounds.right = currentLoc.h + offsetY;
  677.         windowWidth = windowBounds.right - windowBounds.left;
  678.         windowHeight = windowBounds.bottom - windowBounds.top;
  679.         
  680.         // Error check the window rectangle
  681.         if (windowWidth < gMinWindowSize.left)
  682.             windowBounds.right = windowBounds.left + gMinWindowSize.left;
  683.         if (windowHeight < gMinWindowSize.top)
  684.             windowBounds.bottom = windowBounds.top + gMinWindowSize.top;
  685.         SetWindowBounds(theWindow, kWindowStructureRgn, &windowBounds);
  686.         // Draws every "resizingDelay" miliseconds, can easily change the interval
  687.         // I've set the interval to 0 for instantaneous drawing, but different apps may want to
  688.         // change the delay (maybe the drawing is quite complex....) or change it during execution
  689.         if (TickCount() - time >= resizingDelay) {
  690.             drawImage(theWindow);
  691.             QDFlushPortBuffer(GetWindowPort(theWindow), GetPortVisibleRegion(GetWindowPort(theWindow), rgnHandle));
  692.             time = TickCount();
  693.         }
  694.         WaitNextEvent( everyEvent, &anEvent, 0, nil );
  695.         processEvent(&anEvent);
  696.         // User may have decided to quit or close the window
  697.         if (gDone || !IsValidWindowPtr(theWindow))
  698.             break;
  699.     }
  700.     DisposeRgn(rgnHandle);
  701.     // Update the window when all is done
  702.     PostEvent(updateEvt, (long)theWindow);
  703. }
  704.  
  705. void processEvent(EventRecord *anEvent)
  706. {
  707.     WindowPtr       evtWind;
  708.     short           clickArea;
  709.     Rect            screenRect;
  710.     RgnHandle        rgnHandle = NewRgn();
  711.  
  712.     // Pretty standard event processing, save the couple checks for the
  713.     // "trick" flag being set
  714.     if (anEvent->what == mouseDown)
  715.     {
  716.         clickArea = FindWindow( anEvent->where, &evtWind );
  717.                 
  718.         if (clickArea == inMenuBar)
  719.             handleMenuSelection(MenuSelect(anEvent->where));
  720.         else if (clickArea == inDrag)
  721.         {
  722.             GetRegionBounds(GetGrayRgn(), &screenRect);
  723.             DragWindow( evtWind, anEvent->where, &screenRect );
  724.         }
  725.         else if (clickArea == inContent)
  726.         {
  727.             if (evtWind != FrontWindow())
  728.                 SelectWindow( evtWind );
  729.         }
  730.         else if (clickArea == inGoAway) {
  731.             if (TrackGoAway( evtWind , anEvent->where )) {
  732.                 disposeWindow(evtWind);
  733.                 adjustMenus();
  734.             }
  735.         }
  736.         else if (clickArea == inGrow) {
  737.             doDynamicResizing(evtWind);
  738.         }
  739.         else if (clickArea == inZoomIn || clickArea == inZoomOut) {
  740.             if (TrackBox (evtWind, anEvent->where, clickArea == inZoomOut ? inZoomOut : inZoomIn)) {
  741.                 ZoomWindow (evtWind, clickArea == inZoomOut ? inZoomOut : inZoomIn, true);
  742.                 drawImage(evtWind);
  743.                 QDFlushPortBuffer(GetWindowPort(evtWind), GetPortVisibleRegion(GetWindowPort(evtWind), rgnHandle));
  744.             }
  745.         }
  746.     }
  747.     else if (anEvent->what == updateEvt)
  748.     {
  749.         evtWind = (WindowPtr)anEvent->message;    
  750.         SetPortWindowPort(evtWind);
  751.                 
  752.         BeginUpdate(evtWind);
  753.         drawImage(evtWind);
  754.         EndUpdate(evtWind);
  755.     }
  756.     else if (anEvent->what == autoKey || anEvent->what == keyDown)
  757.         handleKeyPress(anEvent);
  758.     else if (anEvent->what == kHighLevelEvent)
  759.         AEProcessAppleEvent(anEvent);
  760.     
  761.     DisposeRgn(rgnHandle);
  762. }
  763.  
  764. void doEventLoop()
  765. {
  766.     EventRecord     anEvent;
  767.     
  768.     while (!gDone)
  769.         if (WaitNextEvent( everyEvent, &anEvent, 10, nil))
  770.             processEvent(&anEvent);
  771. }
  772.